#!/usr/bin/python
# -*- coding:utf-8 -*-

# 用于为c++的结构体自动生成DumpJson函数，存放与同级目录下，文件名称加Dump
import os,sys,re
from string import Template

dict_ori_type = {}
dict_member_note = {}
dict_typeenum_note = {}
dict_typeenum_alis = {}

# 循环读取C++头文件,解析成struct 对应的真实字段类型
def read_header(file):
    in_struct = False
    dict_member = {}
    dict_struct = {}
    dict_struct_note = {}
    dict_member_note = {}
    dict_enum_alis = {}
    dict_enum_note = {}
    note = ''
    lines = open(file,'r+',encoding='gbk').readlines()
    for line in lines:
        m = re.match(r'^#include (.*)', line)
        if m:
            filepath,filename = os.path.split(file)
            newfile = m.group(1).replace('"','')
            read_header(filepath + '/' + newfile)
            continue
        m = re.match(r'^typedef +(.*) +([^\[\]]*)(.*);', line)
        if m:
            if m.group(3).find('[') >= 0:
                dict_ori_type[m.group(2)] = m.group(1) + 's'
            else:
                dict_ori_type[m.group(2)] = m.group(1)
            if len(dict_enum_note) > 0:
                dict_typeenum_note[m.group(2)] = dict_enum_note
            if len(dict_enum_alis) > 0:
                dict_typeenum_alis[m.group(2)] = dict_enum_alis
            dict_enum_note = {}
            dict_enum_alis = {}
            continue
        m = re.match(r'[ \t]*/// *(.*)', line)
        if m:
            note = m.group(1)
            continue
        m = re.match(r'^#define +(.+) +(.+)', line)
        if m:
            enumvalue = m.group(2).replace('\'','')
            dict_enum_note[m.group(1)] = note
            dict_enum_alis[enumvalue] = m.group(1)
            note = ''
            continue
        m = re.match(r'^struct (.*)', line)
        if m:
            in_struct = True
            class_name = m.group(1)
            continue
        if in_struct == True:
            if re.match(r'.*};',line):
                in_struct = False;
                dict_member = {}
                dict_member_note = {}
            else:
                m = re.match(r'[ \t]*([^/]+?)[ \t]+([^/]+);.*', line)
                if m:
                    varname = m.group(2)
                    ori_type = dict_ori_type[m.group(1)]
                    dict_member[varname] = m.group(1)
                    dict_struct[class_name] = dict_member
                    dict_member_note[varname] = note
                    dict_struct_note[class_name] = dict_member_note
                    note = ''
    return dict_struct,dict_ori_type, dict_struct_note, dict_typeenum_note


def write_lines(file, lines):
    fo = open(file, 'w',encoding='gbk')
    for line in lines:
        fo.write(line + '\n')
    fo.close()

#生成用于注释的map
def generate_note_map( pathname, typeenums):
    data_list = []
    filepath,filename = os.path.split(pathname)
    newfilename = filename.split('.')[0]+'Note.h'
    data_list.append('/////////////////////////////////////////////////////////////////////////')
    data_list.append('///@brief 定义了数据类型的枚举值MAP')
    data_list.append('///@author 自动化脚步cppstructdump.py生成')
    data_list.append('/////////////////////////////////////////////////////////////////////////')
    data_list.append('\n')
    data_list.append('#ifndef ' + newfilename.upper().replace('.','_')+'_')
    data_list.append('#define ' + newfilename.upper().replace('.','_')+'_')
    data_list.append('#include <map>')
    data_list.append('#include <string>')
    data_list.append('#include \"'+filename+'\"')
    data_list.append('\n')
    for type in typeenums:
        if len(typeenums[type]) > 0:
            data_list.append('class ' + type + 'Map : public std::map<' + type + ', std::string>')
            data_list.append('{')
            data_list.append('public:')
            data_list.append('    ' + type + 'Map()')
            data_list.append('    {')
            for enum in typeenums[type]:
                data_list.append('        insert(std::make_pair(' + enum +', \"' + typeenums[type][enum] + '\"));')
            data_list.append('    }')
            data_list.append('};')
            data_list.append('static ' + type + 'Map map' + type + ';')
            data_list.append('\n')
    data_list.append('#endif')
    write_lines(filepath+'/'+newfilename,data_list)
    return data_list

#生成专门用于Dump结构体为String的函数头文件
def generate_dump_function( pathname, structs, ori_types):
    data_list = []
    filepath,filename = os.path.split(pathname)
    newfilename = filename.split('.')[0]+'Dump.h'
    data_list.append('/////////////////////////////////////////////////////////////////////////')
    data_list.append('///@brief 定义了数据结构体转换为json字符串的函数')
    data_list.append('///@author 自动化脚步cppstructdump.py生成')
    data_list.append('/////////////////////////////////////////////////////////////////////////')
    data_list.append('\n')
    data_list.append('#ifndef ' + newfilename.upper().replace('.','_')+'_')
    data_list.append('#define ' + newfilename.upper().replace('.','_')+'_')
    data_list.append('#include <string>')
    data_list.append('#include \"'+filename+'\"')
    data_list.append('\n')
    data_list.append('#define CHECKCHAR(chType)   (((char)chType)==0?\' \':(char)(chType))')
    data_list.append('\n')
    for struct in structs:
        data_list.append('static std::string Dump(' + struct + '& data)')
        data_list.append('{')
        data_list.append('    std::string result = \"{\";')
        index = 0
        for member in structs[struct]:
            data_list.append('    result += \"\\\"' + member + '\\\":\";')
            alistype = structs[struct][member]
            if ori_types[alistype] == 'chars':
                data_list.append('    result += \"\\\"\";')
                data_list.append('    result += data.' + member +';')
                data_list.append('    result += \"\\\"\";')
            elif ori_types[alistype] == 'char':
                data_list.append('    result += \"\\\"\";')
                data_list.append('    result += CHECKCHAR(data.' + member + ');')
                data_list.append('    result += \"\\\"\";')
            else:
                data_list.append('    result += std::to_string(data.' + member + ');')

            if index != len(structs[struct])-1:
                data_list.append('    result += \",\";')
            index += 1
        data_list.append('    result += \"}\";')
        data_list.append('    return result;')
        data_list.append('}')
        data_list.append('\n')
    data_list.append('#endif')
    write_lines(filepath+'/'+newfilename,data_list)
    return data_list

#生成专门用于Dump结构体注释为String的函数头文件
def generate_dump_detail_function( pathname, structs, ori_types, structmembernotes, typeenums):
    data_list = []
    filepath,filename = os.path.split(pathname)
    newfilename = filename.split('.')[0]+'DumpDetail.h'
    notefilename = filename.split('.')[0]+'Note.h'
    data_list.append('/////////////////////////////////////////////////////////////////////////')
    data_list.append('///@brief 定义了数据结构体转换为json字符串的函数,采用详细的字段描述')
    data_list.append('///@author 自动化脚步cppstructdump.py生成')
    data_list.append('/////////////////////////////////////////////////////////////////////////')
    data_list.append('\n')
    data_list.append('#ifndef ' + newfilename.upper().replace('.','_')+'_')
    data_list.append('#define ' + newfilename.upper().replace('.','_')+'_')
    data_list.append('#include <string>')
    data_list.append('#include \"'+filename+'\"')
    data_list.append('#include \"'+notefilename+'\"')
    data_list.append('\n')
    data_list.append('#define CHECKCHAR(chType)   (((char)chType)==0?\' \':(char)(chType))')
    data_list.append('\n')
    for struct in structs:
        data_list.append('static std::string DumpDetail(' + struct + '& data)')
        data_list.append('{')
        data_list.append('    std::string result = \"{\";')
        index = 0
        membernotes = structmembernotes[struct]
        for member in structs[struct]:
            alistype = structs[struct][member]
            if member in membernotes:
                data_list.append('    result += \"\\\"' + membernotes[member] + '\\\":\";')
            else:
                data_list.append('    result += \"\\\"' + member + '\\\":\";')

            if ori_types[alistype] == 'chars':
                data_list.append('    result += \"\\\"\";')
                data_list.append('    result += data.' + member +';')
                data_list.append('    result += \"\\\"\";')
            elif ori_types[alistype] == 'char':
                data_list.append('    result += \"\\\"\";')
                if alistype in typeenums:
                    data_list.append('    result += map' + alistype + '[data.' + member + '];')
                else:
                    data_list.append('    result += CHECKCHAR(data.' + member + ');')
                data_list.append('    result += \"\\\"\";')
            else:
                data_list.append('    result += std::to_string(data.' + member + ');')

            if index != len(structs[struct])-1:
                data_list.append('    result += \",\";')
            index += 1
        data_list.append('    result += \"}\";')
        data_list.append('    return result;')
        data_list.append('}')
        data_list.append('\n')
    data_list.append('#endif')
    write_lines(filepath+'/'+newfilename,data_list)
    return data_list

if __name__ == '__main__':
    filepath = sys.argv[1]
    structs, ori_types, structmembernotes, typeenums = read_header(filepath)
    generate_note_map(filepath, typeenums)
    generate_dump_function(filepath, structs, ori_types)
    generate_dump_detail_function(filepath, structs, ori_types, structmembernotes, typeenums)

